#ifndef HC_MD4_H  
#define HC_MD4_H
#include <stdint.h>
#include <string.h>
#include <stdio.h>
#define ROTL32(x, n) (((x) << (n)) | ((x) >> (32 - (n))))
typedef struct {
    uint32_t state[4];
    uint8_t buffer[64];
    uint32_t count[2];
} MD4_CTX;

class hcMD4
{
public:
	
		static void MD4_Hash(PBYTE input,int len,PBYTE output)
		{
			MD4_CTX ctx;
			md4_init(&ctx);
		    md4_update(&ctx, input, len);
		    md4_final(&ctx, output);
		    //return 16; // MD4 always outputs 16 bytes
		}
		static string MD4_Hash_str(const string str)
		{
			unsigned char p[34];
			memset(p,0,34);
			MD4_Hash((PBYTE)str.c_str(),str.length(),p);
			string str1;
			hcStr::pbyte2hexString(p,16,false,str1);
			return str1;
		}

		static void md4_init(MD4_CTX *ctx)
		{
			ctx->state[0] = 0x67452301;
			ctx->state[1] = 0xEFCDAB89;
			ctx->state[2] = 0x98BADCFE;
			ctx->state[3] = 0x10325476;
			ctx->count[0] = ctx->count[1] = 0;
		}
private:
		static void md4_transform(uint32_t state[4], const uint8_t block[64]) {
		    uint32_t a = state[0], b = state[1], c = state[2], d = state[3];
		    uint32_t x[16];

		    // Decode 64-byte block into 16 words
		    for (int i = 0; i < 16; i++)
		        x[i] = (uint32_t)block[i * 4] | ((uint32_t)block[i * 4 + 1] << 8) |
		        ((uint32_t)block[i * 4 + 2] << 16) | ((uint32_t)block[i * 4 + 3] << 24);

		    // Round 1
		#define FF(a, b, c, d, k, s) a = ROTL32(a + ((b & c) | (~b & d)) + x[k], s)
		    FF(a, b, c, d, 0, 3); FF(d, a, b, c, 1, 7); FF(c, d, a, b, 2, 11); FF(b, c, d, a, 3, 19);
		    FF(a, b, c, d, 4, 3); FF(d, a, b, c, 5, 7); FF(c, d, a, b, 6, 11); FF(b, c, d, a, 7, 19);
		    FF(a, b, c, d, 8, 3); FF(d, a, b, c, 9, 7); FF(c, d, a, b, 10, 11); FF(b, c, d, a, 11, 19);
		    FF(a, b, c, d, 12, 3); FF(d, a, b, c, 13, 7); FF(c, d, a, b, 14, 11); FF(b, c, d, a, 15, 19);

		    // Round 2
		#define GG(a, b, c, d, k, s) a = ROTL32(a + ((b & c) | (b & d) | (c & d)) + x[k] + 0x5A827999, s)
		    GG(a, b, c, d, 0, 3); GG(d, a, b, c, 4, 5); GG(c, d, a, b, 8, 9); GG(b, c, d, a, 12, 13);
		    GG(a, b, c, d, 1, 3); GG(d, a, b, c, 5, 5); GG(c, d, a, b, 9, 9); GG(b, c, d, a, 13, 13);
		    GG(a, b, c, d, 2, 3); GG(d, a, b, c, 6, 5); GG(c, d, a, b, 10, 9); GG(b, c, d, a, 14, 13);
		    GG(a, b, c, d, 3, 3); GG(d, a, b, c, 7, 5); GG(c, d, a, b, 11, 9); GG(b, c, d, a, 15, 13);

		    // Round 3
		#define HH(a, b, c, d, k, s) a = ROTL32(a + (b ^ c ^ d) + x[k] + 0x6ED9EBA1, s)
		    HH(a, b, c, d, 0, 3); HH(d, a, b, c, 8, 9); HH(c, d, a, b, 4, 11); HH(b, c, d, a, 12, 15);
		    HH(a, b, c, d, 2, 3); HH(d, a, b, c, 10, 9); HH(c, d, a, b, 6, 11); HH(b, c, d, a, 14, 15);
		    HH(a, b, c, d, 1, 3); HH(d, a, b, c, 9, 9); HH(c, d, a, b, 5, 11); HH(b, c, d, a, 13, 15);
		    HH(a, b, c, d, 3, 3); HH(d, a, b, c, 11, 9); HH(c, d, a, b, 7, 11); HH(b, c, d, a, 15, 15);

		    state[0] += a; state[1] += b; state[2] += c; state[3] += d;
		}
public:
		static void md4_update(MD4_CTX* ctx, const uint8_t* input, size_t len) {
		    uint32_t i, index = (ctx->count[0] >> 3) & 0x3F;

		    ctx->count[0] += (uint32_t)len << 3;
		    if (ctx->count[0] < (uint32_t)len << 3) ctx->count[1]++;
		    ctx->count[1] += (uint32_t)len >> 29;

		    for (i = 0; i < len; i++) {
		        ctx->buffer[index++] = input[i];
		        if (index == 64) {
		            md4_transform(ctx->state, ctx->buffer);
		            index = 0;
		        }
		    }
		}

		static void md4_final(MD4_CTX* ctx, uint8_t digest[16]) {
		    uint8_t padding[64] = { 0x80 };
		    uint32_t bits[2];

		    bits[0] = ctx->count[0];
		    bits[1] = ctx->count[1];

		    uint32_t index = (ctx->count[0] >> 3) & 0x3F;
		    uint32_t padLen = (index < 56) ? (56 - index) : (120 - index);
		    md4_update(ctx, padding, padLen);
		    md4_update(ctx, (uint8_t*)bits, 8);

		    for (int i = 0; i < 4; i++) {
		        digest[i * 4] = (uint8_t)(ctx->state[i] & 0xFF);
		        digest[i * 4 + 1] = (uint8_t)((ctx->state[i] >> 8) & 0xFF);
		        digest[i * 4 + 2] = (uint8_t)((ctx->state[i] >> 16) & 0xFF);
		        digest[i * 4 + 3] = (uint8_t)((ctx->state[i] >> 24) & 0xFF);
		    }
		}
};

#endif

